package com.xishu.service;

import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.Charge;
import com.stripe.model.Customer;
import com.stripe.model.PaymentIntent;
import com.stripe.model.Refund;
import com.stripe.model.terminal.Location;
import com.stripe.model.terminal.Reader;
import com.stripe.param.CustomerCreateParams;
import com.stripe.param.PaymentIntentCaptureParams;
import com.stripe.param.PaymentIntentCreateParams;
import com.stripe.param.RefundCreateParams;
import com.stripe.param.terminal.LocationCreateParams;
import com.stripe.param.terminal.ReaderCreateParams;
import com.xishu.config.Config;
import com.xishu.entity.Company;
import com.xishu.entity.customer.VipRenewEmailTemplate;
import com.xishu.entity.order.Order;
import com.xishu.entity.shop.Shop;
import com.xishu.entity.shop.StripeLocation;
import com.xishu.entity.shop.StripeReader;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.order.base.OrderService;
import com.xishu.util.AES;
import com.xishu.util.Tools;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static com.xishu.config.Constant.*;
import static com.xishu.response.ResponseStatus.STRIPE_NOT_CONFIG;

public class StripeService implements Tools {
    private static Logger logger = LoggerFactory.getLogger(StripeService.class);
    private CommonService commonService = CommonService.getInstance();
    private OrderService orderService = OrderService.getInstance();
    private Config config = Config.getInstance();
    private static StripeService instance = new StripeService();

    private StripeService() {
    }

    public static StripeService getInstance() {
        return instance;
    }

    /**
     * 创建支付信息 必须同步调用，因为apiKey是全局的变量
     *
     * @param orderId
     * @return
     */
    public PaymentIntent createPaymentIntent(Long shopId, Long orderId) throws Exception {
        Order order = orderService.findOrder(shopId, orderId);
        Shop shop = ShopService.getInstance().findShopById(shopId);

        VerifyUtil.verify(() -> isNotEmpty(shop.getStripeSk()), ResponseStatus.STRIPE_CONFIG_NOT_COMPLETE);
        logger.info("shop is {}", shop);
        Stripe.apiKey = AES.decrypt(shop.getStripeSk());

        String customerId = createCustomerId();

        PaymentIntentCreateParams.Builder builder = PaymentIntentCreateParams.builder();
        long userCostLong = showMoneyDouble(order.getUserCost() * 100l).longValue();
        builder.setAmount(userCostLong).
                setCurrency(getCurrency()).setCustomer(customerId);
        builder.setSetupFutureUsage(PaymentIntentCreateParams.SetupFutureUsage.ON_SESSION);
        builder.setDescription(String.format("shop:%s, order perDay no:%s, orderId:%s", shop.getName(), order.getOrderNo(), order.getId()));
        builder.putMetadata("orderId", String.valueOf(orderId));
        builder.putMetadata("shopId", String.valueOf(shop.getId()));
        PaymentIntentCreateParams params = builder.build();
        PaymentIntent paymentIntent = PaymentIntent.create(params);

        return paymentIntent;

    }

    /**
     * 创建支付信息 必须同步调用，因为apiKey是全局的变量
     *
     * @param orderId
     * @return
     */
    public PaymentIntent createPaymentIntent_companyId(Long companyId, Long orderId) throws Exception {
        Order order = orderService.findOrder(orderId);

        Company company = CompanyService.getInstance().findCompanyById(companyId);
        VipRenewEmailTemplate vipRenewEmailTemplate = CompanyService.getInstance().findVipRenewEmailTemplate(companyId);

        VerifyUtil.verify(() -> isNotEmpty(vipRenewEmailTemplate.getStripeSk()), ResponseStatus.STRIPE_CONFIG_NOT_COMPLETE);
        logger.info("vipRenewEmailTemplate is {}", vipRenewEmailTemplate);
        Stripe.apiKey = AES.decrypt(vipRenewEmailTemplate.getStripeSk());

        String customerId = createCustomerId();

        PaymentIntentCreateParams.Builder builder = PaymentIntentCreateParams.builder();
        long userCostLong = showMoneyDouble(order.getUserCost() * 100l).longValue();
        builder.setAmount(userCostLong).
                setCurrency(getCurrency()).setCustomer(customerId);
        builder.setSetupFutureUsage(PaymentIntentCreateParams.SetupFutureUsage.ON_SESSION);
        builder.setDescription(String.format("shop:%s, order perDay no:%s, orderId:%s", company.getName(), order.getOrderNo(), order.getId()));
        builder.putMetadata("orderId", String.valueOf(orderId));
        builder.putMetadata("companyId", String.valueOf(companyId));
        PaymentIntentCreateParams params = builder.build();
        PaymentIntent paymentIntent = PaymentIntent.create(params);

        return paymentIntent;

    }


    /**
     * 创建一个customerId
     *
     * @return
     */
    public String createCustomerId() throws StripeException {
        CustomerCreateParams.Builder builder = CustomerCreateParams.builder();
        Customer customer = Customer.create(builder.build());
        return customer.getId();
    }

    /**
     * 退款，apiKey全局，必须加锁
     *
     * @param order 订单
     * @throws Exception
     */
    public void refunds(Order order) throws Exception {
        Long shopId = order.getShopId();
        Shop shop = ShopService.getInstance().findShopById(shopId);

        synchronized (GlobalService.GLOBAL_LOCK) {
            VerifyUtil.verify(() -> isNotEmpty(shop.getStripeSk()), ResponseStatus.STRIPE_CONFIG_NOT_COMPLETE);
            VerifyUtil.verify(() -> isNotEmpty(order.getStripePaymentId()), ResponseStatus.SYSTEM_ERROR);

            Stripe.apiKey = AES.decrypt(shop.getStripeSk());
            logger.info("will refunds for the payment id {} order id {}", order.getStripePaymentId(), order.getId());
            Refund.create(RefundCreateParams.builder().setPaymentIntent(order.getStripePaymentId()).build());
            logger.info("will refunds for the payment id {} order id {} end", order.getStripePaymentId(), order.getId());
        }
    }

    /**
     * 获取paymentId
     *
     * @param payload
     * @return
     */
    public String getPaymentId(String payload) {
        JSONObject json = JSONObject.fromObject(payload);
        return json.getJSONObject("data").getJSONObject("object").getString("payment_intent");
    }

    /**
     * 获取币种
     *
     * @return
     */
    public String getCurrency() {
        String currency = null;

        if (config.isUsVersion()) {
            currency = "usd";
        } else {
            currency = "hkd";
        }

        return currency;
    }

    public String getCurrency(long shopId) {
        //测试环境
        if (Config.getInstance().isAliyun() && shopId == 31l) {
            return "hkd";
        } else {
            return getCurrency();
        }
    }

    /**
     * 创建地址
     */
    public void createLocation(StripeLocation stripeLocation) throws Exception {
        Location location = null;
        Shop shop = ShopService.getInstance().findShopById(stripeLocation.getShopId());

        synchronized (GlobalService.GLOBAL_LOCK) {
            //如果没有填写stripe信息，报错。
            VerifyUtil.verify(() -> isNotEmpty(shop.getStripeSk()), STRIPE_NOT_CONFIG);
            Stripe.apiKey = AES.decrypt(shop.getStripeSk());
            LocationCreateParams.Address address = LocationCreateParams.Address.builder()
                    .setLine1(stripeLocation.getLine1())
                    .setCity(stripeLocation.getCity())
                    .setState(stripeLocation.getState())
                    .setCountry(stripeLocation.getCountry())
                    .setPostalCode(stripeLocation.getZipCode())
                    .build();

            LocationCreateParams params = LocationCreateParams.builder()
                    .setDisplayName(stripeLocation.getDisplayName())
                    .setAddress(address)
                    .build();

            location = Location.create(params);
            stripeLocation.setStripeId(location.getId());
            stripeLocation.setCompanyId(shop.getCompanyId());
        }

        //添加到数据库
        commonService.createObject(stripeLocation);
    }

    /**
     * 更新位置
     *
     * @param stripeLocation
     */
    public void updateLocation(StripeLocation stripeLocation) throws Exception {
        Shop shop = ShopService.getInstance().findShopById(stripeLocation.getShopId());
        synchronized (GlobalService.GLOBAL_LOCK) {
            Stripe.apiKey = AES.decrypt(shop.getStripeSk());
            Location location = Location.retrieve(stripeLocation.getStripeId());

            Map<String, Object> params = new HashMap<>();
            params.put("display_name", stripeLocation.getDisplayName());
            Map<String, String> addressMap = new HashMap<String, String>();
            addressMap.put("city", stripeLocation.getCity());
            addressMap.put("country", stripeLocation.getCountry());
            addressMap.put("line1", stripeLocation.getLine1());
            addressMap.put("postal_code", stripeLocation.getZipCode());
            addressMap.put("state", stripeLocation.getState());
            params.put("address", addressMap);

            location.update(params);
        }
    }

    /**
     * 删除位置
     *
     * @param stripeLocation
     */
    public void deleteLocation(StripeLocation stripeLocation) throws Exception {
        Shop shop = ShopService.getInstance().findShopById(stripeLocation.getShopId());
        synchronized (GlobalService.GLOBAL_LOCK) {
            Stripe.apiKey = AES.decrypt(shop.getStripeSk());
            Location location = Location.retrieve(stripeLocation.getStripeId());
            location.delete();
        }
    }

    /**
     * 查询读卡机是否存在
     *
     * @param shopId
     * @param label
     * @return
     */
    public Optional<StripeReader> findReader(Long shopId, String label) {
        StripeReader stripeReader = new StripeReader();
        stripeReader.setShopId(shopId);
        stripeReader.setLabel(label);
        return commonService.findOneOptional(stripeReader);
    }

    /**
     * 查询所有的刷卡机
     *
     * @param locationId
     * @return
     */
    public List<StripeReader> findAllReader(Long shopId, Long locationId) {
        StripeReader stripeReader = new StripeReader();
        stripeReader.setStripeLocationId(locationId);
        stripeReader.setShopId(shopId);
        return commonService.searchAll(stripeReader);
    }

    /**
     * 获取stripe里面的位置ID
     *
     * @param locationId
     * @return
     */
    public String readStripeLocationId(Long locationId) throws Exception {
        StripeLocation stripeLocation = commonService.findEntity(locationId, StripeLocation.class);
        return stripeLocation.getStripeId();
    }

    /**
     * 创建一个读卡器
     *
     * @param stripeReader
     */
    public void createReader(StripeReader stripeReader) throws Exception {
        Long locationId = stripeReader.getStripeLocationId();
        String stripeLocationId = readStripeLocationId(locationId);

        ReaderCreateParams params = ReaderCreateParams.builder()
                .setRegistrationCode(stripeReader.getRegistrationCode())
                .setLabel(stripeReader.getLabel())
                .setLocation(stripeLocationId).build();

        Shop shop = ShopService.getInstance().findShopById(stripeReader.getShopId());
        synchronized (GlobalService.GLOBAL_LOCK) {
            Stripe.apiKey = AES.decrypt(shop.getStripeSk());
            Reader reader = Reader.create(params);
            stripeReader.setStripeId(reader.getId());
        }

        commonService.createObject(stripeReader);
    }

    /**
     * 更新读卡器
     *
     * @param stripeReader
     */
    public void updateReader(StripeReader stripeReader) throws Exception {
        Shop shop = ShopService.getInstance().findShopById(stripeReader.getShopId());
        synchronized (GlobalService.GLOBAL_LOCK) {
            Stripe.apiKey = AES.decrypt(shop.getStripeSk());
            Reader reader = Reader.retrieve(stripeReader.getStripeId());
            Map<String, Object> params = new HashMap<>();
            params.put("label", stripeReader.getLabel());
            reader.update(params);
        }
    }

    /**
     * 删除读卡器
     *
     * @param stripeReader
     */
    public void deleteReader(StripeReader stripeReader) throws Exception {
        Shop shop = ShopService.getInstance().findShopById(stripeReader.getShopId());
        synchronized (GlobalService.GLOBAL_LOCK) {
            Stripe.apiKey = AES.decrypt(shop.getStripeSk());
            Reader reader = Reader.retrieve(stripeReader.getStripeId());
            reader.delete();
        }
    }

    /**
     * 获取银行卡信息
     *
     * @param paymentIntentOptional
     * @return
     */
    public String getBrand(Optional<PaymentIntent> paymentIntentOptional) {
        if (!paymentIntentOptional.isPresent()) {
            logger.info("the payment is empty");
            return "";
        }

        return getBrand(paymentIntentOptional.get());
    }

    /**
     * 获取银行卡信息
     *
     * @param paymentIntent
     * @return
     */
    public String getBrand(PaymentIntent paymentIntent) {
        try {
            String brand = paymentIntent.getCharges().getData().get(0).getPaymentMethodDetails().getCardPresent().getBrand();
            return brand;
        } catch (Exception e) {
            logger.error("e", e);
            return "";
        }
    }

    /**
     * 检查最低的支付
     *
     * @param userCost
     * @return
     */
    public boolean checkCost(double userCost) {
        if (Config.getInstance().isUsVersion()) {
            return userCost >= 0.5;
        } else {
            return userCost >= 4;
        }
    }

    /**
     * 支付小费
     * <p>
     * 返回false，表示扣款失败
     * 返回true,表示不需要扣款，或者扣款成功
     *
     * @param orderInDb
     * @throws Exception
     */
    public boolean capture(Order orderInDb, Shop shop) {
        try {
            logger.info("capture shop id {} order {}", shop.getId(), orderInDb.getId());
            String paymentId = orderInDb.getStripePaymentId();
            if (config.mockStripeCaptureError()) {
                orderInDb.setStripeErrorMsg("System Mock Error Msg");
                return false;
            }

            if (isEmpty(paymentId)) {
                logger.info("payment is empty");
                return true;
            }

            if (getDouble(orderInDb.getTipsFee()) <= 0) {
                logger.info("no tips to order");
                return true;
            }

            if (getBoolean(orderInDb.getTipsVerify())) {
                logger.info("order tips verify");
                return true;
            }

            synchronized (GlobalService.GLOBAL_LOCK) {
                Stripe.apiKey = AES.decrypt(shop.getStripeSk());

                PaymentIntent resource = PaymentIntent.retrieve(paymentId);
                long captureAmount = doubleToLong(orderInDb.getUserCost());
                PaymentIntentCaptureParams params = null;

                //如果要mock的时候，不做真实的处理
                if (!config.mockCaptureOk()) {
                    if (config.testCapture()) {
                        params = PaymentIntentCaptureParams.builder().setAmountToCapture(config.captureAmount()).build();
                    } else {
                        params = PaymentIntentCaptureParams.builder().setAmountToCapture(captureAmount).build();
                    }

                    logger.info("{} capture");
                    PaymentIntent paymentIntent = resource.capture(params);
                    //正常capture之后就返回了
                    logger.info("payment intent is {}", paymentIntent);
                }

                //修改订单状态
                orderInDb.setTipsVerify(true);
                commonService.save(orderInDb);
                return true;
            }
        } catch (StripeException e) {
            logger.error("e", e);

            if (e.getStripeError() != null) {
                orderInDb.setStripeErrorMsg(e.getStripeError().getMessage());
            } else {
                orderInDb.setStripeErrorMsg("System Error");
            }

            return false;
        } catch (Exception e) {
            logger.error("e", e);
            orderInDb.setStripeErrorMsg("System Error");
            return false;
        }
    }

    /**
     * 接收支付方式
     *
     * @param paymentId
     * @param shopId
     * @return
     */
    public Optional<PaymentIntent> retrievePaymentIntent(String paymentId, Long shopId) throws Exception {
        if (isEmpty(paymentId)) {
            logger.info("payment id is empty");
            return Optional.empty();
        }

        if (shopId == null) {
            logger.info("shopId is empty");
            return Optional.empty();
        }

        Shop shop = ShopService.getInstance().findShopById(shopId);

        synchronized (GlobalService.GLOBAL_LOCK) {
            try {
                Stripe.apiKey = AES.decrypt(shop.getStripeSk());
                PaymentIntent paymentIntent = PaymentIntent.retrieve(paymentId);
                if (paymentIntent == null) {
                    return Optional.empty();
                }

                return Optional.of(paymentIntent);
            } catch (Exception e) {
                logger.error("e", e);
                return Optional.empty();
            }
        }
    }

    /**
     * 获取打印机ID
     *
     * @param paymentIntentOptional
     * @return
     */
    public Long getPrintId(Optional<PaymentIntent> paymentIntentOptional) {
        if (!paymentIntentOptional.isPresent()) {
            logger.info("can not find the payment intent");
            return null;
        }

        PaymentIntent paymentIntent = paymentIntentOptional.get();
        String printId = paymentIntent.getMetadata().get("printId");
        if (isEmpty(printId)) {
            logger.info("do not have printer id");
            return null;
        }

        return Long.valueOf(printId);
    }

    /**
     * 获取持卡人姓名
     *
     * @param paymentIntentOptional
     * @return
     */
    public String getCardHolderName(Optional<PaymentIntent> paymentIntentOptional) {
        if (!paymentIntentOptional.isPresent()) {
            logger.info("paymentIntentOptional empty");
            return StringUtils.EMPTY;
        }

        return getCardHolderName(paymentIntentOptional.get());
    }

    /**
     * 获取持卡人姓名
     *
     * @param paymentIntent
     * @return
     */
    public String getCardHolderName(PaymentIntent paymentIntent) {
        List<Charge> data = paymentIntent.getCharges().getData();
        if (isEmpty(data)) {
            logger.info("charge data is empty");
            return StringUtils.EMPTY;
        }

        for (Charge charge : data) {
            logger.info("charge id {}", charge.getId());

            Charge.PaymentMethodDetails paymentMethodDetails = charge.getPaymentMethodDetails();
            if (paymentMethodDetails == null) {
                logger.info("paymentMethodDetails empty");
                continue;
            }

            Charge.PaymentMethodDetails.CardPresent cardPresent = paymentMethodDetails.getCardPresent();
            if (cardPresent == null) {
                logger.info("cardPresent empty");
                continue;
            }

            String cardholderName = cardPresent.getCardholderName();
            if (isEmpty(cardholderName)) {
                logger.info("card holder name empty");
                continue;
            }

            logger.info("card holder name {}", cardholderName);
            return cardholderName;
        }

        return StringUtils.EMPTY;
    }

    /**
     * 获取卡号后4位
     *
     * @param paymentIntentOptional
     * @return
     */
    public String getLast4(Optional<PaymentIntent> paymentIntentOptional) {
        if (!paymentIntentOptional.isPresent()) {
            logger.info("paymentIntent empty");
            return StringUtils.EMPTY;
        }

        return getLast4(paymentIntentOptional.get());
    }

    /**
     * 获取后4位
     *
     * @param paymentIntent
     * @return
     */
    public String getLast4(PaymentIntent paymentIntent) {
        List<Charge> data = paymentIntent.getCharges().getData();
        if (isEmpty(data)) {
            logger.info("charge data is empty");
            return StringUtils.EMPTY;
        }

        for (Charge charge : data) {
            logger.info("charge id {}", charge.getId());

            Charge.PaymentMethodDetails paymentMethodDetails = charge.getPaymentMethodDetails();
            if (paymentMethodDetails == null) {
                logger.info("paymentMethodDetails empty");
                continue;
            }

            Charge.PaymentMethodDetails.CardPresent cardPresent = paymentMethodDetails.getCardPresent();
            if (cardPresent == null) {
                logger.info("cardPresent empty");
                continue;
            }

            String last4 = cardPresent.getLast4();
            if (last4 == null) {
                logger.info("last4 empty");
                return StringUtils.EMPTY;
            }

            return last4;
        }

        return StringUtils.EMPTY;
    }

    /**
     * 是否是stripe相关的支付
     *
     * @param orderInDb
     * @return
     */
    public boolean isStripePay(Order orderInDb) {
        if (isEmpty(orderInDb.getStripePaymentId())) {
            logger.info("do not has stripe payment id");
            return false;
        }

        if (getBoolean(orderInDb.getStripePay())) {
            return true;
        }

        if (getBoolean(orderInDb.getGooglePay())
                || getBoolean(orderInDb.getApplePay())
                || getBoolean(orderInDb.getMasterCardPay())
                || getBoolean(orderInDb.getVisaPay())) {
            logger.info("google pay , apple pay, master card pay, visa pay");
            return true;
        }

        Integer payType = getInt(orderInDb.getPayType());
        if (Arrays.asList(ORDER_PAY_TYPE_MASTER, ORDER_PAY_TYPE_VISA, ORDER_PAY_TYPE_APPLE, ORDER_PAY_TYPE_GOOGLE).contains(payType)) {
            logger.info("google pay , apple pay, master card pay, visa pay");
            return true;
        }

        if (payType == ORDER_PAY_TYPE_STRIPE) {
            logger.info("stripe pay");
            return true;
        }

        logger.info("not stripe pay");
        return false;
    }

    /**
     * 订单是否是stripe终端刷卡
     *
     * @param order
     * @return
     */
    public boolean isStripeTerminal(Order order) {
        //不是stripe支付，肯定不是stripe终端支付
        if (!isStripePay(order)) {
            return false;
        }

        return getBoolean(order.getStripeTerminal());
    }

    /**
     * 取消付款
     *
     * @param shopId    分店ID
     * @param paymentId 付款ID
     * @throws StripeException
     */
    public void cancelPayment(Long shopId, String paymentId) throws Exception {
        if (isEmpty(paymentId)) {
            logger.info("paymentId is empty");
            return;
        }

        synchronized (GlobalService.GLOBAL_LOCK) {
            Shop shop = ShopService.getInstance().findShopById(shopId);
            Stripe.apiKey = AES.decrypt(shop.getStripeSk());
            logger.info("cancel request to stripe");
            PaymentIntent paymentIntent = PaymentIntent.retrieve(paymentId);

            if (!paymentIntent.getStatus().toLowerCase().contains("cancel")) {
                paymentIntent.cancel();
            }

        }
    }
}
